﻿using OWL.Rendering.HRP;
using ShaderVariantCollector;
using System;
using System.Collections;
using System.Collections.Generic;
using com.yoozoo.gta.Common;
using UnityEngine;
using UnityEngine.Rendering;
using Yoozoo.Core.Common;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Yoozoo.Mars.Got;
using Yoozoo.UI.YGUI;

namespace Yoozoo.Framework.Managers
{
    //简单的Shader缓存，查询类，实际意义不大
    public class ShaderMgr
    {
        private static Dictionary<string, Shader> shaderCache = new Dictionary<string, Shader>();

        private class ShaderVariantCompileTask
        {
            private string currentCompilingShaderVariantName;
            private int currentCompilingShaderVariantMaxCount;
            private int currentCompilingShaderVariantIndex;
            private int levelVariantCount = 1;
            private bool isCompiling = false;
            private bool isCompilingInProgress = false;
            private bool isComplete = false;

            private Action<float> progressCallback;
            private Action completeCallback;

            private bool isCompilingLevelVariant = false;
            private bool isLevelVariantComplete = false;

            private int currentQualityLevel = -1;

            private List<ShaderVariantCollection> waitingToBeWarmUpSVC = new List<ShaderVariantCollection>();

            /// <summary>
            /// 已经预热过的SVC，持有，避免被GC
            /// </summary>
            private List<ShaderVariantCollection> warmedUpShaderVariantCollections = new List<ShaderVariantCollection>();

            private bool canWarmUp = false;

            private List<ResLoader> loaders = new List<ResLoader>(10);
            
            public void Update()
            {
                if (isCompiling)
                {
                    if (!canWarmUp)
                    {
                        canWarmUp = true;
                    }
                    else
                    {
                        var totalCount = currentCompilingShaderVariantMaxCount + levelVariantCount;
                        if (waitingToBeWarmUpSVC.Count > 0)
                        {
                            var index = waitingToBeWarmUpSVC.Count - 1;
                            var svc = waitingToBeWarmUpSVC[index];
                            svc.WarmUp(); ;
                            waitingToBeWarmUpSVC.RemoveAt(index);
                            
                            // var loader = loaders[index];
                            // loader.ReleaseAllRes();
                            // loader.Recycle2Cache();
                            // loaders.RemoveAt(index);
                            
                            warmedUpShaderVariantCollections.Add(svc);
                            currentCompilingShaderVariantIndex = currentCompilingShaderVariantIndex + 1;
                            progressCallback?.Invoke((float)currentCompilingShaderVariantIndex / (currentCompilingShaderVariantMaxCount + levelVariantCount));
                            //等一帧，防止进度条卡死
                            canWarmUp = false;
                        }
                        else
                        {
                            if (currentCompilingShaderVariantIndex >= totalCount)
                            {
                                isCompiling = false;
                                completeCallback?.Invoke();
                                completeCallback = null;
                                progressCallback = null;
                                isComplete = true;
                            }
                        }
                    }
                }
            }
            
            private void LoadShaderVariant(string name, int index)
            {
                currentCompilingShaderVariantIndex = index;
                currentCompilingShaderVariantName = name;
                isCompilingInProgress = true;
                var resLoader = ResLoader.Alloc();
                resLoader.Add2Load(string.Format("Assets/ResourcesAssets/Shaders/{0}_{1}.shadervariants", name, index),
                    ( success,assetName, asset) =>
                    {
                        try
                        {
                            if (success)
                            {
                                var svc = ((ShaderVariantCollection) asset);
                                waitingToBeWarmUpSVC.Add(svc);
                            }
                            else
                            {
                                UnityEngine.Debug.LogErrorFormat("compile {0}_{1}.shadervariants 失败",name,index);
                            }
                        }
                        catch (Exception e)
                        {
                            UnityEngine.Debug.LogErrorFormat("compile {0}_{1}.shadervariants 失败",name,index);
                        }
                    });
                resLoader.Load();
                loaders.Add(resLoader);
            }

            public void LoadShaderVariantLevel(bool warmUpSync = false)
            {
                var name = currentCompilingShaderVariantName;
                int level = (int)QualityHelper.CurrentAdjusterSetting.quality;
                if (level == currentQualityLevel)
                {
                    return;
                }
                currentQualityLevel = level;
                isCompilingInProgress = true;
                var resLoader = ResLoader.Alloc();
                resLoader.Add2Load(string.Format("Assets/ResourcesAssets/Shaders/{0}_level{1}.shadervariants",name,level),
                    ( success,assetName, asset) =>
                    {
                        try
                        {
                            if (success)
                            {
                                var svc = ((ShaderVariantCollection) asset);
                                waitingToBeWarmUpSVC.Add(svc);
                            }
                            else
                            {
                                UnityEngine.Debug.LogErrorFormat("compile {0}_{1}.shadervariants 失败",name,level);
                            }
                        }
                        catch (Exception e)
                        {
                            UnityEngine.Debug.LogErrorFormat("compile {0}_{1}.shadervariants 失败",name,level);
                        }
                    });
                resLoader.Load();
                loaders.Add(resLoader);
            }

            public void Init(string name, Action<float> progressCallback, Action completeCallback)
            {
                this.progressCallback = progressCallback;
                this.completeCallback = completeCallback;
                this.currentCompilingShaderVariantName = name;
                isComplete = false;
                waitingToBeWarmUpSVC.Clear();
            }

            public void StartTask(int maxCount)
            {
                currentCompilingShaderVariantMaxCount = maxCount;
                currentCompilingShaderVariantIndex = 0;
                //全部加载
                for (int i = 0; i < maxCount; i++)
                {
                    LoadShaderVariant(this.currentCompilingShaderVariantName,i);
                }
                LoadShaderVariantLevel();
                isCompiling = true;
                canWarmUp = true;
            }
        }


        public static void OnQualityChanged()
        {
            currentSceneShaderTask?.LoadShaderVariantLevel(true);
        }
        
        private static Dictionary<string, ShaderVariantCompileTask> tasks = new Dictionary<string,ShaderVariantCompileTask>();

        private static bool loadingShaderVariantRoot;
        private static GameObject shaderVariantRoot;
        private static ShaderVariantData shaderVariantData;
        private static ShaderVariantCompileTask currentSceneShaderTask;
        private static ResLoader shaderVariantRootResLoader;
        private static List<Action> shaderVariantRootLoadOverActions = new List<Action>();
        
        public static void Update()
        {
            foreach (var item in tasks.Values)
            {
                item.Update();
            }
        }

        private static bool initedDeviceInfo = false;
        private static bool isPowerVR;
        
        private static List<string> shaderCollections = new List<string>(10);

        private static Dictionary<string, int> shaderCollectionMap = new Dictionary<string, int>(10);
        
        public static void PreWarmUp(int enterType, Action callBack,Action processNoticeAction = null)
        {
            if (enterType == 1) // 首次登录银行（新号未完成银行场景）
            {
                shaderCollections = new List<string>()
                {
                    "LoadingVariantCollection",
                    // "BankVariantCollection",
                    "PveVariantCollection",
                    //"CityVariantCollection",  // 有个小Loading时长可接受
                    "HangingVariantCollection",
                };
            }
            else if (enterType == 2) //  序章街道阶段（完成了银行，没去过内城）
            {
                shaderCollections = new List<string>()
                {
                    "LoadingVariantCollection",
                    "PveVariantCollection",
                    "CityVariantCollection",
                    "HangingVariantCollection",
                };
            }
            else if (enterType == 3) // 章节引导阶段（去过内城，但未解锁沙盘）
            {
                shaderCollections = new List<string>()
                {
                    "LoadingVariantCollection",
                    "PveVariantCollection",
                    "CityVariantCollection",
                    //"HangingVariantCollection",
                };
            }
            else if (enterType == 4) // 解锁世界阶段（已经进过沙盘）
            {
                shaderCollections = new List<string>()
                {
                    "LoadingVariantCollection",
                    "PveVariantCollection",
                    "CityVariantCollection",
                    //"HangingVariantCollection",
                    "WorldVariantCollection",
                };
            }
            
            Timers.inst.StartCoroutine(PreWarmUpCoroutine(callBack,processNoticeAction));
        }

        private static IEnumerator PreWarmUpCoroutine(Action callBack,Action processNoticeAction)
        {
            int index = 0;
            bool loadStep = false;
            while (index < shaderCollections.Count)
            {
                if (!loadStep)
                {
                    loadStep = true;
                    LoadVariant(shaderCollections[index], () =>
                    {
                        index++;
                        loadStep = false;
                        if (processNoticeAction != null)
                        {
                            processNoticeAction.Invoke();
                        }
                    });
                }
                yield return null;
            }
            yield return null;
            callBack?.Invoke();
        }


        private static void LoadVariant(string name, Action completeFunc, Action<float> progressFunc = null)
        {
            if (!initedDeviceInfo)
            {
                initedDeviceInfo = true;
                isPowerVR = SystemInfo.graphicsDeviceName != null && SystemInfo.graphicsDeviceName.Contains("PowerVR");
            }

            int qualityLevel = (int)QualityHelper.CurrentAdjusterSetting.quality;
            shaderCollectionMap[name] = qualityLevel;

            var needSkip = isPowerVR || qualityLevel < 3;
            needSkip = needSkip || UnityEngine.Application.platform == RuntimePlatform.IPhonePlayer;  //IOS跳过试一下
            if (needSkip)
            {
                completeFunc.Invoke();
                return;
            }
            if (!tasks.TryGetValue(name, out var task))
            {
                task = new ShaderVariantCompileTask();
                task.Init(name,progressFunc,completeFunc);
                tasks.Add(name,task);
            }
            else
            {
                completeFunc?.Invoke();
            }
            currentSceneShaderTask = task;
            if (shaderVariantRoot == null)
            {
                void Action()
                {
                    for (int i = 0; i < shaderVariantData.shaderVariantsName.Count; i++)
                    {
                        if (shaderVariantData.shaderVariantsName[i] == name)
                        {
                            task.StartTask(shaderVariantData.shaderVariantsCount[i]);
                            return;
                        }
                    }

                    completeFunc?.Invoke();
                }
                shaderVariantRootLoadOverActions.Add(Action);
                if (loadingShaderVariantRoot)
                {
                    
                }
                else
                {
                    loadingShaderVariantRoot = true;
                    shaderVariantRootResLoader = ResLoader.Alloc();
                    shaderVariantRootResLoader.Add2Load(
                        "Assets/ResourcesAssets/Prefabs/ShaderVariantData/ShaderVariantData.prefab",
                        (success,assetName,asset) =>
                        {
                            loadingShaderVariantRoot = false;
                            shaderVariantRoot = GameObject.Instantiate(asset as GameObject);
                            shaderVariantData = shaderVariantRoot.GetComponent<ShaderVariantData>();
                            if (shaderVariantData)
                            {
                                shaderVariantData.onUpdate = ShaderMgr.Update;
                                for (int i = 0; i < shaderVariantRootLoadOverActions.Count; i++)
                                {
                                    shaderVariantRootLoadOverActions[i].Invoke();
                                }
                                shaderVariantRootLoadOverActions.Clear();
                            }
                        });
                    shaderVariantRootResLoader.Load();
                }
            }
            else
            {
                for (int i = 0; i < shaderVariantData.shaderVariantsName.Count; i++)
                {
                    if (shaderVariantData.shaderVariantsName[i] == name)
                    {
                        task.StartTask(shaderVariantData.shaderVariantsCount[i]);
                        return;
                    }
                }
                completeFunc?.Invoke();
            }
        }
        
        public static void LoadLoadingVariantCollectionsShaders(Action completeFunc)
        {
            GgameShaderVariantCollector.EnterScene("Loading");
            
            if (shaderCollectionMap.ContainsKey("LoadingVariantCollection") && 
                shaderCollectionMap["LoadingVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
            {
                completeFunc?.Invoke();
                return;
            }
            LoadVariant("LoadingVariantCollection",completeFunc);
            shaderCollections.Add("LoadingVariantCollection");
        }
        public static void LoadWorldVariantCollectionsShaders(Action completeFunc,Action<float> progressFunc = null)
        {
            GgameShaderVariantCollector.EnterScene("World");
            if (shaderCollectionMap.ContainsKey("WorldVariantCollection") && 
                shaderCollectionMap["WorldVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
            {
                completeFunc?.Invoke();
                progressFunc?.Invoke(100);
                return;
            }
            LoadVariant("WorldVariantCollection",completeFunc,progressFunc);
            shaderCollections.Add("WorldVariantCollection");
        }

        private static string GetShaderQualityNameByQuality(string shaderVariantName)
        {
            var shaderQuality = QualityManager.Inst.GetMarkQualityListener<ShaderQualityListener>("ShaderQualityListener");
            int quality = -1;
            if (shaderQuality != null)
            {
                quality = (int)shaderQuality.CurrentQualitySetting.Quality;
            }
            
            if (quality >= 0)
            {
                shaderVariantName = shaderVariantName + quality;
            }
            return shaderVariantName;
        }
        
        public static void LoadPVEVariantCollectionsShaders(Action completeFunc,Action<float> progressFunc = null)
        {
            GgameShaderVariantCollector.EnterScene("Pve");
            if (shaderCollectionMap.ContainsKey("PveVariantCollection") && 
                shaderCollectionMap["PveVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
            {
                completeFunc?.Invoke();
                progressFunc?.Invoke(100);
                return;
            }
            LoadVariant("PveVariantCollection",completeFunc,progressFunc);
            shaderCollections.Add("PveVariantCollection");
        }
        
        public static void LoadHangingVariantCollectionShaders(Action completeFunc,Action<float> progressFunc = null)
        {
            GgameShaderVariantCollector.EnterScene("Hanging");
            if (shaderCollectionMap.ContainsKey("HangingVariantCollection") && 
                shaderCollectionMap["HangingVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
            {
                completeFunc?.Invoke();
                progressFunc?.Invoke(100);
                return;
            }
            LoadVariant("HangingVariantCollection",completeFunc,progressFunc);
            shaderCollections.Add("HangingVariantCollection");
        }

        // public static void LoadGuideHangingVariantCollectionShaders(Action completeFunc,
        //     Action<float> progressFunc = null)
        // {
        //     GgameShaderVariantCollector.EnterScene("Hanging");
        //     if (shaderCollectionMap.ContainsKey("GuideHangingVariantCollection") && 
        //         shaderCollectionMap["GuideHangingVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
        //     {
        //         completeFunc?.Invoke();
        //         progressFunc?.Invoke(100);
        //         return;
        //     }
        //     LoadVariant("GuideHangingVariantCollection",completeFunc,progressFunc);
        //     shaderCollections.Add("GuideHangingVariantCollection");
        // }
        
        public static void LoadBankVariantCollectionShaders(Action completeFunc,Action<float> progressFunc = null)
        {
            GgameShaderVariantCollector.EnterScene("Bank");
            if (shaderCollectionMap.ContainsKey("BankVariantCollection") && 
                shaderCollectionMap["BankVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
            {
                completeFunc?.Invoke();
                progressFunc?.Invoke(100);
                return;
            }
            LoadVariant("BankVariantCollection",completeFunc,progressFunc);
            shaderCollections.Add("BankVariantCollection");
        }

        public static void LoadCityVariantCollectionsShaders(Action completeFunc,Action<float> progressFunc = null)
        {
            GgameShaderVariantCollector.EnterScene("City");
            if (shaderCollectionMap.ContainsKey("CityVariantCollection") && 
                shaderCollectionMap["CityVariantCollection"] == (int)QualityHelper.CurrentAdjusterSetting.quality)
            {
                completeFunc?.Invoke();
                progressFunc?.Invoke(100);
                return;
            }
            LoadVariant("CityVariantCollection",completeFunc,progressFunc);
            shaderCollections.Add("CityVariantCollection");
        }

        private static Dictionary<Type, Shader> VolumeComponentToShaderMap;
        private static Shader uberPost;
        private static HashSet<Type> prewarmedVolumeComponentType = new HashSet<Type>();

        private static void PrewarmShader(Shader shader)
        {
            try
            {
                var svc = new ShaderVariantCollection();
                var sv = new ShaderVariantCollection.ShaderVariant {shader = shader};
                svc.Add(sv);
                svc.WarmUp();
                Debug.Log("预热shader成功"+shader.name);
            }
            catch (Exception e)
            {
                Debug.LogError("预热shader失败"+shader.name);
            }
        }
        
        public static void LoadPostProcessShaders(HBaseRenderScene renderScene)
        {
            if (VolumeComponentToShaderMap == null)
            {
                VolumeComponentToShaderMap = new Dictionary<Type, Shader>();
                var renderSettingsManager = HRenderSettingsManager.Get();
                var shaderCollections = renderSettingsManager.RenderSettingsData.shaderCollections;
                VolumeComponentToShaderMap.Add(typeof(HHeightFog),shaderCollections.heightFog);
                VolumeComponentToShaderMap.Add(typeof(HRadialBlur),shaderCollections.radialBlur);
                VolumeComponentToShaderMap.Add(typeof(HStroke),shaderCollections.stroke);
                VolumeComponentToShaderMap.Add(typeof(HLightShaft),shaderCollections.lightShaft);
                VolumeComponentToShaderMap.Add(typeof(HTiltShiftBlurDOF),shaderCollections.tiltShiftBlurDOF);
                VolumeComponentToShaderMap.Add(typeof(HSSR),shaderCollections.SSR);
                uberPost = shaderCollections.uberPost;
                PrewarmShader(uberPost);
            }
            var postEffectStyle = QualityHelper.CurrentAdjusterSetting.postEffectStyle;
            Volume volume = null;
            var volumeQualityListener = renderScene.GetComponentInChildren<VolumeQualityListener>();
            var styleSettings = volumeQualityListener.styleSettings;
            if (styleSettings != null)
            {
                for (int i = 0; i < styleSettings.Count; i++)
                {
                    var style = styleSettings[i];
                    if (style.style == postEffectStyle)
                    {
                        volume = style.Volume;
                        break;
                    }
                }
            }

            if (volume != null && volume.sharedProfile != null)
            {
                var components = volume.sharedProfile.components;
                for (int i = 0; i < components.Count; i++)
                {
                    var component = components[i];
                    if (component.active)
                    {
                        var type = component.GetType();
                        if (!prewarmedVolumeComponentType.Contains(type))
                        {
                            if (VolumeComponentToShaderMap.TryGetValue(type, out var s))
                            {
                                PrewarmShader(s);
                                prewarmedVolumeComponentType.Add(type);
                            }
                        }
                    }
                }
            }
           
        }

        public static void LoadYTextMaterial()
        {
            UMTResource.LoadAsset("Assets/ResourcesAssets/UI/Material/mat_uguiFont.mat",
                (assetName, asset, duration, userData) =>
                {
                    var mat = (Material) asset;
                    YText.SetNewTextMaterial(mat);
                });
        }
        public static void BuildCache(Shader[] shaders)
        {
            for (int i = 0; i < shaders.Length; i++)
            {
                var curShader = shaders[i];
#if UNITY_EDITOR
                if (curShader.name.StartsWith("FairyGUI"))
                {
                    continue;
                }
                if (curShader.name.StartsWith("Mobile/RT"))
                {
                    continue;
                }
#endif
                shaderCache[curShader.name] = curShader;
            }
        }

        public static Shader Find(string name)
        {
            if (shaderCache == null)
            {
                shaderCache = new Dictionary<string, Shader>();
            }

            if (!shaderCache.ContainsKey(name))
            {
                Shader target = Shader.Find(name);
                if (target == null)
                {
                    Debug.LogError("Shader not exist: " + name);
                    return null;
                }
                shaderCache[name] = target;
            }

            return shaderCache[name];
        }
    }
}
